'''
Created on 16/11/2013

@author: lior
'''
import pc


class AbstractSexpr:
    @staticmethod
    def readFromString(s):
        ps = pc.ParserStack()

        ''' toSkipParser Parser'''
        ps.const(lambda x: x <= ' ')
        WhiteSpace = ps.done()

        ps.const(lambda x: x == ';')
        ps.const(lambda x: x != '\n')
        ps.star()
        ps.pack(lambda x: ''.join(x))
        ps.const(lambda x: x == '\n')
        ps.catens(3)
        ps.pack(lambda x: ''.join(x))
        LineComment = ps.done()

        ps.const(lambda x: x == '#')
        ps.const(lambda x: x == ';')
        ps.delayed_parser(lambda: Sexpr)
        ps.catens(3)
        SexprComment = ps.done()

        ps.parser(WhiteSpace)
        ps.parser(LineComment)
        ps.parser(SexprComment)
        ps.disjs(3)
        Comments = ps.done()

        ps.parser(Comments)
        ps.star()
        toSkipParser = ps.done()

        '''booleanParser Parser'''
        ps.const(lambda x: x == '#')
        ps.const(lambda x: x == 't' or x == 'T' or x == 'f' or x == 'F')
        ps.caten()
        ps.pack(lambda x: x[1] == 't' or x[1] == 'T')
        ps.pack(lambda x: Boolean(x))
        booleanParser = ps.done()

        '''numberParser Parser'''
        ps.const(lambda x: x == '0')
        Zero = ps.done()

        ps.const(lambda x: x >= '1' and x <= '9')
        D1 = ps.done()

        ps.parser(D1)
        ps.const(lambda x: x >= 'a' and x <= 'f')
        ps.const(lambda x: x >= 'A' and x <= 'F')
        ps.disjs(3)
        HexD1 = ps.done()

        ps.parser(Zero)
        ps.parser(D1)
        ps.disj()
        D0 = ps.done()

        ps.parser(Zero)
        ps.parser(HexD1)
        ps.disj()
        HexD0 = ps.done()

        ps.parser(Zero)
        ps.const(lambda x: x == 'x' or x == 'X' or x == 'h' or x == 'H')
        ps.caten()
        Hex = ps.done()

        ps.parser(Hex)
        ps.parser(Zero)
        ps.star()
        ps.pack(lambda x: ''.join(x))
        ps.parser(HexD1)
        ps.parser(HexD0)
        ps.star()
        ps.pack(lambda x: ''.join(x))
        ps.catens(4)
        ps.pack(lambda x: int(x[1] + x[2] + x[3], base=16))
        HexNat = ps.done()

        ps.parser(Zero)
        ps.star()
        ps.pack(lambda x: ''.join(x))
        ps.parser(D1)
        ps.parser(D0)
        ps.star()
        ps.pack(lambda x: ''.join(x))
        ps.catens(3)
        ps.pack(lambda x: int(x[0] + x[1] + x[2]))
        DecNat = ps.done()

        ps.parser(DecNat)
        ps.parser(HexNat)
        ps.disj()
        Nat = ps.done()

        ps.parser(Nat)
        ps.const(lambda x: x == '+')
        ps.parser(Nat)
        ps.caten()
        ps.pack(lambda x: x[1])
        ps.const(lambda x: x == '-')
        ps.parser(Nat)
        ps.caten()
        ps.pack(lambda x: -x[1])
        ps.parser(Zero)
        ps.plus()
        ps.pack(lambda x: int(''.join(x)))
        ps.disjs(4)
        ps.pack(lambda x: Integer(x))
        integerParser = ps.done()

        ps.parser(integerParser)
        ps.const(lambda x: x == '/')
        ps.parser(Nat)
        ps.catens(3)
        ps.pack(lambda x: Fraction(x[0], x[2]))
        fractionParser = ps.done()

        ps.parser(fractionParser)
        ps.parser(integerParser)
        ps.disj()
        ps.parser(pc.pcWhite1)
        ps.caten()
        ps.pack(lambda x: x[0])
        numberParser = ps.done()

        '''symbolParser Parser'''
        ps.const(lambda x: x >= 'a' and x <= 'z')
        ps.const(lambda x: x >= 'A' and x <= 'Z')
        ps.const(lambda x: x >= '0' and x <= '9')
        ps.const(lambda
                x: x == '!' or x == '$' or x == '^' or x == '*' or x == '-' or x == '_' or x == '=' or x == '+' or x == '<' or x == '>' or x == '/' or x == '?')
        ps.disjs(4)
        ps.plus()
        ps.pack(lambda x: ''.join(x))
        ps.pack((lambda x: Symbol(x)))
        symbolParser = ps.done()

        '''stringParser Parser'''
        ps.const(lambda x: x == chr(92))
        ps.const(lambda x: x == 'n' or x == 'r' or x == 't' or x == 'f' or x == '"' or x == chr(92) or x == 'l')
        ps.caten()
        ps.pack(lambda x: ''.join(x))
        ps.pack(lambda x:
        {'\\n': chr(10), '\\r': chr(13), '\\t': chr(9), '\\f': chr(12), '\\\\': '\\', '\\"': chr(34), '\\l': 955}[
            x])
        metaChars = ps.done()

        ps.const(lambda x: x == '"')
        ps.parser(metaChars)
        ps.const(lambda x: x != '"')
        ps.disj()
        ps.star()
        ps.pack(lambda x: ''.join(x))
        ps.const(lambda x: x == '"')
        ps.catens(3)
        ps.pack(lambda x: String(x[1]))
        stringParser = ps.done()



        '''Named Char'''
        ps.wordCI("newline")
        ps.wordCI("return")
        ps.wordCI("tab")
        ps.wordCI("page")
        ps.wordCI("lambda")
        ps.disjs(5)
        ps.pack(lambda  x:''.join(x))
        ps.pack(lambda  x:{'newline':10, 'return':13, 'tab':9, 'page':12, 'lambda':955}[x])
        namedCharParser = ps.done()

        ''' Hexadecimal chars '''
        ps.const(lambda x: x>='0' and x<='9')
        ps.const(lambda x: x>='a' and x<='f')
        ps.const(lambda x: x>='A' and x<='F')
        ps.disjs(3)
        oneHexa=ps.done()
        ps.parser(oneHexa)
        ps.parser(oneHexa)
        ps.caten()
        ps.pack(lambda x:''.join(x))
        twoHexa=ps.done()
        ps.parser(twoHexa)
        ps.parser(twoHexa)
        ps.caten()
        ps.pack(lambda x:''.join(x))
        fourHexa=ps.done()

        ps.const(lambda x: x=='x')
        ps.parser(fourHexa)
        ps.parser(twoHexa)
        ps.disj()
        ps.caten()
        ps.pack(lambda x: int(x[1], base=16))
        HexadecimalCharsParser=ps.done()


        ''' Visible chars '''
        ps.const(lambda x: x>=' ')
        ps.pack(lambda x:ord(x))
        VisibleCharsParser=ps.done()

        '''Char Parser'''
        ps.const(lambda x: x=='#')
        ps.const(lambda x: x==chr(92))
        ps.caten()
        ps.parser(namedCharParser)
        ps.parser(HexadecimalCharsParser)
        ps.parser(VisibleCharsParser)
        ps.disjs(3)
        ps.caten()
        ps.pack(lambda x:Char(x[1]))
        charParser=ps.done()


        '''nilParser Parser'''
        ps.const(lambda x: x == '(')
        ps.parser(toSkipParser)
        ps.const(lambda x: x == ')')
        ps.catens(3)
        ps.pack(lambda x: Nil())
        nilParser = ps.done()

        '''pairParser Parser'''

        def listToPairs(s, a):
            if s:
                return Pair(s[0], listToPairs(s[1:], a))
            else:
                return a


        ps.const(lambda x: x == '(')
        ps.delayed_parser(lambda: Sexpr)
        ps.star()
        ps.const(lambda x: x == ')')
        ps.catens(3)
        ps.pack(lambda x: listToPairs(x[1], Nil()))
        ProperList = ps.done()

        ps.const(lambda x: x == '(')
        ps.delayed_parser(lambda: Sexpr)
        ps.plus()
        ps.const(lambda x: x == '.')
        ps.delayed_parser(lambda: Sexpr)
        ps.const(lambda x: x == ')')
        ps.catens(5)
        ps.pack(lambda x: listToPairs(x[1][:-1], Pair.makeDottedPair(x[1][-1], x[3])))
        ImproperList = ps.done()

        ps.parser(ProperList)
        ps.parser(ImproperList)
        ps.disj()
        pairParser = ps.done()

        '''vectorParser Parser'''
        ps.const(lambda x: x == '#')
        ps.const(lambda x: x == '(')
        ps.delayed_parser(lambda: Sexpr)
        ps.star()
        ps.const(lambda x: x == ')')
        ps.catens(4)
        ps.pack(lambda x: Vector(x[2]))
        vectorParser = ps.done()

        '''Quote Parser'''

        ps.const(lambda x: x == ',')
        ps.const(lambda x: x == '@')
        ps.caten()
        ps.pack(lambda x: "UnquotedSpliced")
        ps.const(lambda x: x == '\'' or x == '`' or x == ',')
        ps.pack(lambda x: {'\'': "Quoted", '`': "QQuoted", ',': "Unquoted"}[x])
        ps.disj()
        ps.delayed_parser(lambda: Sexpr)
        ps.caten()
        ps.pack(lambda x: Pair.makeDottedPair(Symbol(x[0]), x[1]))
        quoteParser = ps.done()

        '''Sexpr Parser'''
        ps.parser(toSkipParser)
        ps.parser(booleanParser)
        ps.parser(numberParser)
        ps.parser(symbolParser)
        ps.parser(stringParser)
        ps.parser(charParser)
        ps.parser(nilParser)
        ps.parser(pairParser)
        ps.parser(vectorParser)
        ps.parser(quoteParser)
        ps.disjs(9)
        ps.parser(toSkipParser)
        ps.catens(3)
        ps.pack(lambda x: x[1])
        Sexpr = ps.done()

        return Sexpr.match(s)


class Boolean(AbstractSexpr):
    def __init__(self, x):
        self.bool = x

    def __str__(self):
        return '#t' if self.bool else '#f'


class Void(AbstractSexpr):
    def __str__(self):
        return '#<void>'


class Nil(AbstractSexpr):
    def __str__(self):
        return '#<nil>'


class Char(AbstractSexpr):
    def __init__(self, x):
        self.char = x

    def __str__(self):
        return self.char


class AbstractNumber(AbstractSexpr):
    pass


class Integer(AbstractNumber):
    def __init__(self, x):
        self.int = x

    def __str__(self):
        return str(self.int)


class Fraction(AbstractNumber):
    def __init__(self, x, y):
        self.numerator = x
        self.denominator = y

    def __str__(self):
        return str(self.numerator) + '/' + str(self.denominator)


class String(AbstractSexpr):
    def __init__(self, s):
        self.str = s

    def __str__(self):
        return '\"' + str(self.str) + '\"'

class Char(AbstractSexpr):
    def __init__(self, c):
        self.ch = c

    def __str__(self):
        return str(chr(self.ch))

class Symbol(AbstractSexpr):
    def __init__(self, s):
        self.sym = s

    def __str__(self):
        return self.sym.upper()


class Pair(AbstractSexpr):
    def __init__(self, left, right):
        self.left = left
        self.right = right
        self.dot = False

    @staticmethod
    def makeDottedPair(left,right):
        ans = Pair(left,right)
        ans.setDot()
        return ans

    def __str__(self):
        ans=''
        if str(self.left)=='QUOTED':
            ans+="'"
        elif str(self.left)=='QQUOTED':
            ans+="`"
        elif str(self.left)=='UNQUOTED':
            ans+=","
        elif str(self.left)=='UNQUOTEDSPLICED':
            ans+=",@"
        if ans!='':
            ans+= str(self.right)
        else:
            ans = '('
            i = self
            while isinstance(i, Pair):
                ans += str(i.left) + (' ' if isinstance(i.right, Pair) else '')
                #if i.dot: #check if improper list
                #    return ans + ' . ' + str(i.right) + ')'
                i = i.right
            if isinstance(i, Nil) == False:
                ans += ' . ' + str(i)
            ans+=')'
        return ans

    def getLeft(self):
        return self.left

    def getRight(self):
        return self.right

    def setDot(self):
        self.dot = True

    def isProper(self):
        i = self
        while isinstance(i, Pair):
            if i.dot:
                return False
            i=i.right
        return True

class Vector(AbstractSexpr):
    def __init__(self, l):
        self.vec = l

    def __str__(self):
        i = 0
        ans = '('
        while i < len(self.vec) - 1:
            ans = ans + (str(self.vec[i])) + ', '
            i += 1

        return ans + str(self.vec[i]) + ')'

#e,r=AbstractSexpr.readFromString('#\\newline')
#print(r)
#print(e)
#print(e.__class__.__name__)